package com.service.atf.reply.ac;

import com.service.atf.base.core.CheckpointInfo;
import com.service.atf.base.core.GlobalDefine;
import com.service.atf.base.core.NIOFileReader;
import com.service.atf.base.exception.TFException;
import com.service.atf.base.model.Account;
import com.service.atf.base.utils.ATFConstants;
import com.service.atf.base.utils.ATFLogger;
import com.luotao.devp.base.utils.ByteUtil;
import com.luotao.devp.base.utils.StringUtils;

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * User: luotao Date: 12-2-25 Time: 18:08
 */
public class AccountManager {

    public static final AccountManager ACCOUNT_MANAGER = new AccountManager();

    private static final int DEFAULT_INDEX_VALUE = -1;

    private static final int AC_PROFILE_LEN = 8;

    int[] index = new int[ATFConstants.AC_MAX_REG_TOTAL];
    byte[][] acountData = new byte[ATFConstants.AC_MAX_LOGIN_TOTAL][GlobalDefine.AC_USER_LEN_EVERY];

    int totalLogin;

    int currentAccountIndex = 0;

    public byte[][] getAllAccount() {
        return acountData;
    }

    public AccountManager() {
        for (int i = 0; i < index.length; i++) {
            index[i] = DEFAULT_INDEX_VALUE;
        }
        showAc();
    }

    private void showAc() {
        ATFLogger.sysdebug("" + totalLogin);
        ATFLogger.sysdebug("" + currentAccountIndex);
    }

    public static AccountManager getInstance() {
        return ACCOUNT_MANAGER;
    }


    public int getCurrentAccountIndex() {
        return currentAccountIndex;
    }

    public void setCurrentAccountIndex(int currentAccountIndex) {
        this.currentAccountIndex = currentAccountIndex;
    }

    public int getTotalLogin() {
        return totalLogin;
    }

    public void setTotalLogin(int totalLogin) {
        this.totalLogin = totalLogin;
    }

    private Account account = new Account();

    public Account getEmptyAccount() {
        account.clear();
        return account;
    }


    public Account getAccount(String codeCode) {
        byte[] data = getAccountBytes(codeCode);

        if (data == null) {
            return null;
        }
        account.clear();
        return account.byte2Object(data);
    }

    public byte[] getAccountBytes(String codeCode) {
        int indexInData = getIntUser(codeCode);
        if (indexInData == DEFAULT_INDEX_VALUE) {
            return null;
        }
        byte[] data = acountData[indexInData];
        return data;
    }


    public void updateAccount(Account Account) {
        String custCode = Account.getCustomerCode();
        int indexInData = getIntUser(custCode);
        acountData[indexInData] = Account.object2Byte();
    }


    public int getIntUser(String customerCode) {
        int custIndex = getCustomerCodeIntValue(customerCode);
        return index[custIndex];
    }


    public int addToAccountByte(Account account) {
        if (totalLogin == ATFConstants.AC_MAX_LOGIN_TOTAL) {
            optimzAccount();
            if (totalLogin == ATFConstants.AC_MAX_LOGIN_TOTAL) {
                String msg = account.getCustomerCode() + " maxLogin : " + ATFConstants.AC_MAX_LOGIN_TOTAL;
                ATFLogger.syserr(msg);
                throw new TFException(ATFConstants.ATF_AC_LOGIN_MAX, msg);
            }
        }
        int indexOfCustom = getCustomerCodeIntValue(account.getCustomerCode());
        byte[] data = account.object2Byte();
        acountData[currentAccountIndex] = data;
        index[indexOfCustom] = currentAccountIndex;
        currentAccountIndex++;
        totalLogin++;
        return 1;
    }



    public void removeCustomer(String customercode) {
        int indexOfCustomer = getCustomerCodeIntValue(customercode);
        int indexInAccountData = index[indexOfCustomer];
        acountData[indexInAccountData] = null;
//    acountData[indexInAccountData]=new byte[GlobalDefine.AC_USER_LEN_EVERY];
        index[indexOfCustomer] = 0;
    }


    public void optimzAccount() {
        byte[][] tempAcountData = new byte[ATFConstants.AC_MAX_LOGIN_TOTAL][GlobalDefine.AC_USER_LEN_EVERY];
        int k = 0;
        for (int i = 0; i < ATFConstants.AC_MAX_LOGIN_TOTAL; i++) {

            byte[] accData = acountData[i];

            if (accData == null || accData[0] == 0) {
                continue;
            }

            tempAcountData[k] = acountData[i];
            k++;
        }
        totalLogin = k;

        currentAccountIndex = k;
        for (int j = 0; j < k; j++) {
            byte[] accData = tempAcountData[j];
            String customerId = ByteUtil.readString(accData, 0, 20);
            int indexOfCustomer = getCustomerCodeIntValue(customerId);
            index[indexOfCustomer] = j;
        }
        acountData = tempAcountData;
        showAc();
    }

    public int getCustomerCodeIntValue(String customercode) {
        customercode = customercode.trim();
        if (customercode.length() < 20) {
            throw new TFException(ATFConstants.ATF_AC_CUSTCODE_DIG_ERR, "");
        }
        String str_userindex = customercode.substring(11);
        int i_userindex = 0;
        try {
            i_userindex = Integer.valueOf(str_userindex);
            if (i_userindex + 1 > ATFConstants.AC_MAX_REG_TOTAL) {
                String msg = "";
                throw new TFException(ATFConstants.ATF_AC_CUSTCODE_REG_ERR, msg);
            }
            return i_userindex;
        } catch (NumberFormatException e) {
            String msg =  customercode;
            ATFLogger.syserr(msg);
            throw new TFException(ATFConstants.ATF_CUST_CODE_ERR, msg);
        }
    }


    public boolean recoveBycheckPoint(CheckpointInfo checkpointInfo) {


        byte[] dataInFile = NIOFileReader.readFileContent(checkpointInfo.getFilename(), ATFConstants.AC_USER_LEN_ALL + AC_PROFILE_LEN);

        totalLogin = ByteUtil.readInt(dataInFile, 0);
        currentAccountIndex = ByteUtil.readInt(dataInFile, 4);

        int userCount = (dataInFile.length - AC_PROFILE_LEN) / GlobalDefine.AC_USER_LEN_EVERY;
        for (int i = 0; i < userCount; i++) {
            account.clear();
            System.arraycopy(dataInFile, AC_PROFILE_LEN + i * GlobalDefine.AC_USER_LEN_EVERY, acountData[i], 0, GlobalDefine.AC_USER_LEN_EVERY);
            account.byte2Object(acountData[i]);
            if (StringUtils.isEmpty(account.getCustomerCode())) {
                continue;
            }
            int indexId = getCustomerCodeIntValue(account.getCustomerCode());
            index[indexId] = i;
        }
        showAc();
        return true;
    }


    public void storeByCheckpoint(String path) {
        try {
            ATFLogger.sysinfo("AC PATH : " + path);
            ATFLogger.sysinfo("AC FULL PATH : " + ATFConstants.AC_FILE_PATH_PREFIX + path);
            RandomAccessFile accfile = new RandomAccessFile(ATFConstants.AC_FILE_PATH_PREFIX + path, "rw");
            FileChannel channel = accfile.getChannel();
            MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0,
                    ATFConstants.AC_MAX_LOGIN_TOTAL * GlobalDefine.AC_USER_LEN_EVERY + 1 + 8);
            buffer.position(0);
            buffer.putInt(totalLogin);
            buffer.putInt(currentAccountIndex);
            for (int i = 0; i < acountData.length; i++) {
                buffer.put(acountData[i]);
            }
            buffer.put((byte) '1');
            channel.close();
            accfile.close();
        } catch (Exception e) {
            String msg = e.toString();
            ATFLogger.syserr(msg);
            throw new TFException(ATFConstants.ATF_CHECKPOINT_STORE_ERR, msg);
        }
    }


    public void unLockAccount(String customercode) {
        byte[] data = getAccountBytes(customercode);
        ByteUtil.write(data, data.length - 1, 0);
    }

    public Account getBussAccount(String customerCode) {
        return null;  //To change body of created methods use File | Settings | File Templates.
    }
}
